﻿String.prototype.format = function () {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] != 'undefined'
      ? args[number]
      : '{' + number + '}'
    ;
    });
};

String.prototype.trimStart = function () {
    if (!arguments || arguments.length == 0)
        arguments = ['\r', '\n', '\t', ' '];
    return this.replace(new RegExp('^' + Array.prototype.splice.call(arguments, 0).join('|'), 'g'), '');
}
String.prototype.trimEnd = function () {
    if (!arguments || arguments.length == 0)
        arguments = ['\r', '\n', '\t', ' '];
    return this.replace(new RegExp(Array.prototype.splice.call(arguments, 0).join('|') + '$', 'g'), '');
}

if (String.prototype.trim == undefined) {
    String.prototype.trim = function () {
        return this.trimStart(arguments).trimEnd(arguments);
    };
}

(function ($) {
    var eventNameChangingField = "fieldChanging",
        eventNameChangedField = "fieldChanged";
    eventNameBindingError = "bindingError";
    Binding = function () {
        this.getValue = function () {
            var parts = this.expression.split('.');
            if(!this.expression)
                return this.target;
            var value = this.target;
            for (var i = 0; i < parts.length; i++) {
                if (value === null)
                    return value;
                value = value[parts[i]];
            }
            if (this.format) {
                if ($.isFunction(this.format))
                    return this.format(value);
                return this.format.format(value);
            }
            return value;
        };


        this.apply = function (elements, doNotRegisterEvents) {
            var binding = this;
            var val = binding.getValue();
            var inputs = elements.filter(':input').val(val)
            if (!doNotRegisterEvents) inputs.change(function () {
                binding.setValue($(this).val(), this);
            });
            elements.filter(':not(:input))').text(val);
        };
        this.setValue = function (value, source, doNotTriggerEvents) {
            var target = this.target;
            var parts = this.expression.split(".");
            if (parts.length > 1) {
                try {
                    var binding = new Binding();
                    binding.target = $(target).getBinding(parts[0]).getValue();
                    parts.shift();
                    binding.expression = parts.join('.');
                    binding.setValue(value, source, doNotTriggerEvents);
                }
                catch (ex) {
                    $(document).triggerHandler(eventNameBindingError, {
                        target: target,
                        field: this.expression,
                        Exception: ex
                    });
                }
            }
            else {
                try {
                    var $target = $(target);
                    var eventArgs = {
                        cancel: false,
                        fieldName: parts[0],
                        source: source
                    };
                    if (!doNotTriggerEvents) $target.triggerHandler(eventNameChangingField, {
                        target: target,
                        eventArgs: eventArgs
                    });
                    if (eventArgs.cancel) return;
                    target[parts[0]] = value;
                    if (!doNotTriggerEvents) $target.triggerHandler(eventNameChangedField, {
                        target: target,
                        eventArgs: {
                            fieldName: parts[0]
                        },
                        source: source
                    });
                }
                catch (ex) {
                    $(document).triggerHandler(eventNameBindingError, {
                        target: target,
                        field: this.expression,
                        Exception: ex,
                        source: source
                    });
                }
            }
        };
    }

    $.extend($.fn, {
        getBinding: function (field) {
            var parts = field.split(".");
            var bindings = this.data('bindings');
            for (var i = 0; bindings && i < bindings.length; i++) {
                if (bindings[i].expression == field) return bindings[i];
            }
        },
        addBinding: function (target, field, options) {
            var binding;
            if (target instanceof Binding) {
                binding = target;
                options = field;
                field=null;
            }
            else {
                if (!$.isArray(field)) field = field.split('.');
                var binding = new Binding();
                var newOpt = {};
                $.extend(newOpt, options, { target: target, expression: field[0] });
                options = newOpt;
            }
            if (options)
            {
                if(typeof(options) == "string")
                    options={format:options};

                $.extend(binding, options);
            }
            var bindings = $(target).data('bindings');
            if (!bindings) $(target).data('bindings', bindings = new Array());
            bindings.push(binding);
            if (field && field.length > 1) {
                var intermediate = binding.getValue();
                field.shift();
                this.addBinding(intermediate, field);
            }
            else this.applyBinding(binding);
        },
        applyBinding: function (binding, doNotRegisterEvents, source) {
            var elements = this;
            if (source) elements = this.not(source);
            binding.apply(elements, doNotRegisterEvents);
            if (!doNotRegisterEvents) $(binding.target).bind(eventNameChangedField, function () {
                elements.applyBinding(binding, true, this);
            });
        },
        setField: function (fieldName, value, source) {
            this.each(function (index, item) {
                var b = new Binding();
                b.target = item;
                b.expression = fieldName;
                b.setValue(value, source);
            });
        }

    });
})(jQuery);

CssBinding = function (key) { return function (elements) { elements.css(key, this.getValue()); }; };
Array.prototype.replace = function (index, item) {
    this[index] = item;
};

ObservableArray = function (array) {
    this.push = function (item) {
        array.push(item);
        $(this).triggerHandler('collectionChanged', {
            action: 'push',
            newItems: [item]
        });
    };
    this.shift = function () {
        var item = array.shift();
        $(this).triggerHandler('collectionChanged', {
            action: 'shift',
            oldItems: [item]
        });
    };
    this.pop = function () {
        var item = array.pop();
        $(this).triggerHandler('collectionChanged', {
            action: 'pop',
            oldItems: [item]
        });
    };
    this.unshift = function (item) {
        array.unshift(item);
        $(this).triggerHandler('collectionChanged', {
            action: 'unshift',
            newItems: [item]
        });
    };
    this.replace = function (index, item) {
        var oldItem = array[index];
        array.replace(index, item);
        $(this).triggerHandler('collectionChanged', {
            action: 'replace',
            newItems: [item],
            oldItems: [oldItem]
        });
    };
    this.toString = function () {
        return array.toString();
    };
};